package admin

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"time"

	"github.com/go-kratos/kratos/v2/errors"
	"gorm.io/gorm"

	v1 "open/api/open/admin/v1"
	"open/internal/model"
	"open/internal/model/event"
	"open/internal/model/field"
	"open/internal/types"
	"open/pkg/alipay"
	"open/pkg/middleware/auth"
	"open/pkg/sync/errgroup"
)

// AuthorizeAlipay 授权支付宝应用
func (s *Service) AuthorizeAlipay(ctx context.Context, req *v1.AlipayAuthorizeRequest) (*v1.AlipayAuthorizeReply, error) {
	userInfo, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, authInfo, err := s.dao.AuthorizeAlipay(ctx, req.AppAuthCode)
	if err != nil {
		if alipayErr, ok := err.(*alipay.ErrorResponse); ok {
			err = errors.BadRequest("ALIPAY_ERROR", alipayErr.Error())
		}
		return nil, err
	}
	if err := s.dao.Transaction(ctx, func(ctx context.Context) error {
		if len(authInfo.Tokens) != 1 {
			return errors.BadRequest("ALIPAY_AUTH_ERROR", "支付宝授权应用错误")
		}
		info := authInfo.Tokens[0]
		// 删除现存跟小程序的关联关系
		if err := s.dao.BrandMiniAppRef(ctx, userInfo.BrandID, info.AuthAppID, types.Alipay); err != nil {
			return err
		}
		// 落库品牌跟小程序关联关系
		if err := s.dao.CreateBrandMiniAppRef(ctx, &model.BrandMiniApp{
			Platform: types.Alipay,
			BrandID:  userInfo.BrandID,
			AppID:    info.AuthAppID,
			EmpID:    userInfo.EmpID,
		}); err != nil {
			return err
		}
		if err := s.dao.CreateAlipayOpenSubApp(ctx, &model.AlipayOpenSubApp{
			AppID:           info.AuthAppID,
			OpenAppID:       appID,
			AppAuthCode:     req.AppAuthCode,
			AppAuthToken:    info.AppAuthToken,
			AppRefreshToken: info.AppRefreshToken,
			UserID:          info.UserID,
			Status:          types.AlipayAuthorized,
		}); err != nil {
			return err
		}
		baseInfo, err := s.dao.GetAlipayMiniBaseInfo(ctx, info.AuthAppID)
		if err != nil {
			return err
		}
		// 落库小程序应用信息
		alipayMiniApp, err := s.dao.GetOrCreateAlipayMiniApp(ctx, info.AuthAppID)
		if err != nil {
			return err
		}
		alipayMiniApp.AppID = info.AuthAppID
		alipayMiniApp.Name = baseInfo.AppName
		alipayMiniApp.Logo = baseInfo.AppLogo
		alipayMiniApp.Slogan = baseInfo.AppSlogan
		alipayMiniApp.AppName = baseInfo.AppName
		alipayMiniApp.AppEnglishName = baseInfo.AppEnglishName
		alipayMiniApp.AppSlogan = baseInfo.AppSlogan
		alipayMiniApp.AppDesc = baseInfo.AppDesc
		alipayMiniApp.ServicePhone = baseInfo.ServicePhone
		if err := s.dao.SaveAlipayMiniApp(ctx, alipayMiniApp); err != nil {
			return err
		}
		// 初始化版本信息
		if err := s.dao.CreateAppVersion(ctx, &model.MiniAppVersion{
			Platform: types.Alipay,
			AppID:    info.AuthAppID,
			Status:   types.StatusAuthorized,
			Stage:    types.StageInitialized,
		}); err != nil {
			return err
		}
		schedule := &model.Schedule{
			RefID:     alipayMiniApp.ID,
			JobType:   types.TaskSyncQRCode,
			JobData:   field.JSON("{}"),
			JobStatus: types.ScheduleTaskInit,
		}
		if err := s.dao.CreateSchedule(ctx, schedule); err != nil {
			return err
		}
		return s.dao.TxSend(ctx, &event.TaskSyncQRCode{
			AppID:     info.AuthAppID,
			Platform:  types.Alipay,
			Timestamp: time.Now().Unix(),
		})
	}); err != nil {
		return nil, err
	}
	return &v1.AlipayAuthorizeReply{Message: "ok"}, nil
}

// AddAlipayMiniTester 新增支付宝体验者
func (s *Service) AddAlipayMiniTester(ctx context.Context, req *v1.AddAlipayMiniTesterRequest) (*v1.AddAlipayMiniTesterReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	if err := s.dao.AddAlipayMiniTester(ctx, appID, req.LogonId); err != nil {
		if alipayError, ok := err.(*alipay.ErrorResponse); ok {
			err = errors.BadRequest("ALIPAY_ERROR", alipayError.SubMsg)
		}
		return nil, err
	}
	return &v1.AddAlipayMiniTesterReply{Message: "ok"}, err
}

// DeleteAlipayMiniTester 删除支付宝体验者
func (s *Service) DeleteAlipayMiniTester(ctx context.Context, req *v1.DeleteAlipayMiniTesterRequest) (*v1.DeleteAlipayMiniTesterReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	if err := s.dao.DeleteAlipayMiniTester(ctx, appID, req.UserId); err != nil {
		if alipayError, ok := err.(*alipay.ErrorResponse); ok {
			err = errors.BadRequest("ALIPAY_ERROR", alipayError.SubMsg)
		}
		return nil, err
	}
	return &v1.DeleteAlipayMiniTesterReply{Message: "ok"}, err
}

// GetAlipayMini 获取支付宝小程序信息
func (s *Service) GetAlipayMini(ctx context.Context, req *v1.GetAlipayMiniRequest) (*v1.GetAlipayMiniReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}

	var (
		testers       []*alipay.AppMemberInfo
		alipayMiniApp *model.AlipayMiniApp
		version       *model.MiniAppVersion
	)
	g := errgroup.WithCancel(ctx)
	g.Go(func(ctx context.Context) error {
		alipayMiniApp, err = s.dao.GetAlipayMiniApp(ctx, appID)
		if err != nil {
			return err
		}
		return nil
	})
	g.Go(func(ctx context.Context) error {
		testers, err = s.dao.GetAlipayMiniTesters(ctx, appID)
		if err != nil {
			return err
		}
		return nil
	})
	g.Go(func(ctx context.Context) error {
		version, err = s.dao.GetAppVersion(ctx, appID, types.Alipay)
		if err != nil {
			return err
		}
		return nil
	})
	err = g.Wait()
	if err != nil {
		return nil, err
	}
	members := make([]*v1.GetAlipayMiniReply_Member, len(testers))
	for i, member := range testers {
		members[i] = &v1.GetAlipayMiniReply_Member{
			UserId:  member.UserID,
			LogonId: member.LogonID,
		}
	}
	return &v1.GetAlipayMiniReply{
		QrCodeUrl: alipayMiniApp.QrCodeURL,
		Name:      alipayMiniApp.Name,
		Logo:      alipayMiniApp.Logo,
		Slogan:    alipayMiniApp.Slogan,
		AppId:     alipayMiniApp.AppID,
		Version: &v1.GetAlipayMiniReply_Version{
			ReleaseVersion: version.ReleaseVersion,
			AuditVersion:   version.AuditVersion,
			RejectReason:   version.RejectReason,
			Status:         version.Status,
			UpdateTime:     version.UpdatedAt.Format("2006-01-02 15:04:05"),
		},
		Members: members,
		AuditInfo: &v1.GetAlipayMiniReply_AuditInfo{
			AppName:           alipayMiniApp.AppName,
			AppEnglishName:    alipayMiniApp.AppEnglishName,
			AppSlogan:         alipayMiniApp.AppSlogan,
			AppLogo:           alipayMiniApp.AppLogo,
			MiniCategoryIds:   alipayMiniApp.MiniCategoryIDs,
			ServicePhone:      alipayMiniApp.ServicePhone,
			AppDesc:           alipayMiniApp.AppDesc,
			LicenseNo:         alipayMiniApp.LicenseNo,
			LicenseName:       alipayMiniApp.LicenseName,
			LicensePic:        alipayMiniApp.LicensePic,
			LicenseValidDate:  alipayMiniApp.LicenseValidDate,
			OutDoorPic:        alipayMiniApp.OutDoorPic,
			SpecialLicensePic: alipayMiniApp.SpecialLicensePic,
		},
	}, nil

}

// AddAlipayMiniInfo 补充支付宝小程序信息
func (s *Service) AddAlipayMiniInfo(ctx context.Context, req *v1.AddAlipayMiniInfoRequest) (*v1.AddAlipayMiniInfoReply, error) {
	appID, err := s.dao.GetAppIDByBrandID(ctx, req.BrandId, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	alipayMiniApp, err := s.dao.GetAlipayMiniApp(ctx, appID)
	if err != nil {
		return nil, err
	}
	alipayMiniApp.AppName = req.AppName
	alipayMiniApp.AppEnglishName = req.AppEnglishName
	alipayMiniApp.AppLogo = req.AppLogo
	alipayMiniApp.AppDesc = req.AppDesc
	alipayMiniApp.AppSlogan = req.AppSlogan
	alipayMiniApp.MiniCategoryIDs = req.MiniCategoryIds
	alipayMiniApp.ServicePhone = req.ServicePhone
	alipayMiniApp.LicenseNo = req.LicenseNo
	alipayMiniApp.LicenseValidDate = req.LicenseValidDate
	alipayMiniApp.OutDoorPic = req.OutDoorPic
	alipayMiniApp.SpecialLicensePic = req.SpecialLicensePic
	err = s.dao.SaveAlipayMiniApp(ctx, alipayMiniApp)
	if err != nil {
		return nil, err
	}
	return &v1.AddAlipayMiniInfoReply{Message: "ok"}, nil
}

// CommitAlipayMini 提交支付宝小程序
func (s *Service) CommitAlipayMini(ctx context.Context, req *v1.CommitAlipayMiniRequest) (*v1.CommitAlipayMiniReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	if err = s.dao.Transaction(ctx, func(ctx context.Context) error {
		appVersion, err := s.dao.LockAppVersion(ctx, appID, types.Alipay)
		if err != nil {
			return err
		}
		appVersion.Stage = types.StageInitialized
		appVersion.Status = types.StatusAuditing
		if err = s.dao.SaveAppVersion(ctx, appVersion); err != nil {
			return err
		}
		template, err := s.dao.GetLatestAlipayTemplate(ctx)
		if err != nil {
			return err
		}
		Ext := struct {
			ExtAppID  string                 `json:"extAppid"`
			Ext       map[string]interface{} `json:"ext"`
			ExtEnable bool                   `json:"extEnable,omitempty"`
		}{
			ExtAppID: appVersion.AppID,
			Ext: map[string]interface{}{
				"brand_id": user.BrandID,
				"app_id":   appID,
			},
			ExtEnable: true,
		}
		ext, _ := json.Marshal(&Ext)
		commitTemplate := template.ToVO(string(ext))
		jobData, _ := json.Marshal(commitTemplate)
		schedule := &model.Schedule{
			JobType:   types.TaskCommitCode,
			RefID:     appVersion.ID,
			JobData:   field.JSON(jobData),
			JobStatus: types.ScheduleTaskInit,
		}
		if err = s.dao.CreateSchedule(ctx, schedule); err != nil {
			return err
		}
		// 发送事件
		if err = s.dao.TxSend(ctx, event.TaskCommitCode{
			AppID:     appVersion.AppID,
			Platform:  appVersion.Platform,
			Biz:       jobData,
			Timestamp: time.Now().Unix(),
		}); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &v1.CommitAlipayMiniReply{Message: "ok"}, nil
}

// GetAlipayMiniTestQRCode 获取支付宝小程序体验码
func (s *Service) GetAlipayMiniTestQRCode(ctx context.Context, _ *v1.GetAlipayMiniTestQRCodeRequest) (*v1.GetAlipayMiniTestQRCodeReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	appVersion, err := s.dao.GetAppVersion(ctx, appID, types.Alipay)
	if err != nil {
		return nil, err
	}
	es, err := s.dao.GetAlipayMiniTestQRCode(ctx, appID, appVersion.AuditVersion)
	if err != nil {
		if bytedanceError, ok := err.(*alipay.ErrorResponse); ok {
			return nil, errors.BadRequest("ALIPAY_ERROR", bytedanceError.SubMsg)
		}
		return nil, err
	}
	if es.Status == "notExpVersion" {
		return nil, errors.BadRequest("APP_NOT_FOUND", "暂无体验版")
	}
	resp, err := http.Get(es.ExpQrCodeURL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	buffer := make([]byte, resp.ContentLength)
	_, _ = io.ReadFull(resp.Body, buffer)
	return &v1.GetAlipayMiniTestQRCodeReply{
		Content:       buffer,
		ContentLength: resp.ContentLength,
		ContentType:   resp.Header.Get("Content-Type"),
	}, nil
}

// CreateAlipayMiniQRCode 创建支付宝小程序二维码
func (s *Service) CreateAlipayMiniQRCode(ctx context.Context, req *v1.CreateAlipayMiniQRCodeRequest) (*v1.CreateAlipayMiniQRCodeReply, error) {
	user, err := auth.FromContext(ctx)
	if err != nil {
		return nil, err
	}
	appID, err := s.dao.GetAppIDByBrandID(ctx, user.BrandID, types.Alipay)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.BadRequest(v1.ErrorCode_AUTH_NOT_FOUND.String(), "支付宝小程序未授权")
		}
		return nil, err
	}
	resp, err := s.dao.CreateAlipayMiniQRCode(ctx, appID, req)
	if err != nil {
		if bytedanceError, ok := err.(*alipay.ErrorResponse); ok {
			return nil, errors.BadRequest("ALIPAY_ERROR", bytedanceError.SubMsg)
		}
		return nil, err
	}
	return &v1.CreateAlipayMiniQRCodeReply{
		QrCodeUrl: resp.QRCodeURL,
	}, nil
}
